#coding: utf-8

"""
    @author: Loopher
    @date: 2020-08-29
    @description: Mange device 
"""
import threading
import os
import codecs
import json
import frida
import sys
import io
import time
# sys.stdout= io.TextIOWrapper(sys.stdout.buffer,encoding="utf8")



class ShellThread(threading.Thread):

    def __init__(self,remote_port):

        super(ShellThread,self).__init__(name="shell_thread")
        self._remote_port = remote_port
        # self._device_serial = device_serial
    
    def run(self)->None:
        try:
            cmd = "adb  forward tcp:{0} tcp:{1}".format(self._remote_port,self._remote_port)
            os.system(cmd)
            print("---> forward port succ ")
        except Exception as identifier:
            print("run command erro ",e)
            # right here check whether error occured ?
            raise e 
    

class RemoteDeviceMgr(object):

    def __new__(cls):
        """
        设备管理 单利模式
        """
        if not hasattr(cls,"instance"):
            cls.instance = super(RemoteDeviceMgr,cls).__new__(cls)
            cls.instance.__init() # initial
        return cls.instance

    def __init(self):

        self._antidbg_config_path = "./cache/current/antidbg-info.json"
        self.error_msg=None # 异常描述信息
        # self._control_settings_path ="./cached/current/device_control.json"  # 控制设备的配置
        self.need_antidbg = True
        if not os.path.isfile(self._antidbg_config_path):
            self.need_antidbg = False
        self._antidbg_config={}
        self._control_settings = {}
        if self.need_antidbg is True:
            with codecs.open(self._antidbg_config_path,'r',encoding="utf-8") as fp:
                self._antidbg_config  = json.load(fp)
        print(self._antidbg_config)

        info  = self._antidbg_config['antidbg']
        if "," in info:
            for  item in info.split(",") :
                # read port 
                if ":" in item :
                    # print(" --->",item)
                    port = item.split(":")[1]
                    self._control_settings[port] = {"network":item,"status":"idle","forward":False} 
        else:
            if ":" in info :
                port = info.split(":")[1]
                self._control_settings[port] = {"network":info,"status":"idle","forward":False}
        if len(self._control_settings.keys()) ==0:
            self.error_msg="Can't get remote device ,you have to check remote address is right?\ncurrent config {0}".format(json.dumps(self._control_settings,indent=4)) 

    def reload_config(self):
        # 每次点击反调试按钮后会重新刷新一次配置信息
        self.__init()

    def get_error(self):

        return self.error_msg

    def update_control(self,device):

        """
        更新设备状态， 由于这里控制方式是通过 host:port的形式，因此这里还是通过这个模式来判断设备是否存在即可
        """
        pass

    def _get_device_by_frida_deviceManger(self,remote):

        try:
            device = frida.get_device_manager().add_remote_device(remote)
            info = device.enumerate_applications() # check it avaliable?
            return device 
        except Exception as e:
            self.error_msg ="Get remote device error  --> remote-> {0} --> error:{1} ".format(remote,e)
            print(self.error_msg)
            return None

    def is_need_antidbg(self):

        return self.need_antidbg

    def get_device(self):
        
        for port,item in self._control_settings.items():
            # 启动端口转发即可
            # print("port-> ",port," item-> ",item)
            # if item['forward'] is False:
            shell = ShellThread(port)
            shell.start()
            time.sleep(2.0) # wait  
            # item['forward']=True
            self._control_settings[port]=item
            device=self._get_device_by_frida_deviceManger(item['network'])
            if device is None:
                # self.error_msg = "Trying obtain remote device {0} failed ,plz check it ".format(item["network"])
                continue
            return device

    def release_device(self):

        self._control_settings={}
        self._antidbg_config={}

        # try:
        #     self._control_settings[port]['forward']=False
        #     self._control_settings[port]['status']="idle"
        # except KeyError as e:
        #     print("Release device erorr ",e)            


    def dump_device_info(self,port=None):
        
        if port is not None:
            try:
                item = self._control_settings[port]
                print("--"*20+" dump device info "+"--"*20)
                print(json.dumps(item,indent=2))
            except KeyError as e:
                print("Not found {0} exists in control settings".format(port))
        else:
            print("--"*20+" dump device info "+"--"*20)
            print(json.dumps(self._control_settings,indent=2))
            
    
def test():
    # device = frida.get_device_manager().add_remote_device("127.0.0.1:1234")
    # try:
    #     d=device.enumerate_applications()
    #     print(d)
    # except Exception as e:
    #     print("",e)
    # print(device)
    device = RemoteDeviceMgr()
    d=device.get_device()
    if d is not None:
        print(d.name,d.type)
    device.dump_device_info()

if __name__ =='__main__':
    test()
